package com.skynet.supervision.aop.validate;


import com.skynet.supervision.utils.CollectionExtUtils;
import com.skynet.supervision.utils.Errors;
import lombok.SneakyThrows;
import lombok.val;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.validation.ValidatorFactory;
import java.util.Objects;

@Aspect
@Component
@Order(98)
public class ValidateAdvice {

    private final Logger logger;
    private final ValidatorFactory factory;


    ValidateAdvice(Logger logger,
                   @Qualifier("hibernate-validator-factory") ValidatorFactory factory) {
        this.logger = logger;
        this.factory = factory;
    }

    @Pointcut("@annotation(com.skynet.supervision.aop.validate.ApplyValidate)")
    private void handleMessage() {
    }

    @Around("handleMessage()")
    @SneakyThrows
    private Object beforeHandleMessage(ProceedingJoinPoint jp) {
        val target = jp.getTarget();
        val args = jp.getArgs();
        val method = ((MethodSignature) jp.getSignature()).getMethod();
        val executableValidator = factory.getValidator().forExecutables();
        val paramConstraints = executableValidator
                .validateParameters(target, method, args);
        if (CollectionExtUtils.notEmpty(paramConstraints)) {
            val validateError = Errors.wrapValidateException(paramConstraints);
            logger.warn("validate input param failed:{}",
                    Objects.requireNonNull(validateError).unwrap().getData());
            throw validateError;
        }
        val out = jp.proceed();
        val outConstraints = executableValidator
                .validateReturnValue(target, method, out);
        if (CollectionExtUtils.notEmpty(outConstraints)) {
            val validateError = Errors.wrapValidateException(outConstraints);
            logger.warn("validate output param failed:{}",
                    Objects.requireNonNull(validateError).unwrap().getData());
            throw validateError;
        }
        return out;
    }
}
